﻿using System.Threading;
using System.Threading.Tasks;

namespace 多线程
{
    class 多线程进阶
    {
        #region 多线程同步
        /*
         * 为什么会有多线程同步这种东西？
         * 当多个线程完成一个大任务时，线程之间的协调时非常重要的，当线程之间需要共享某些信息时，那么就要用到多线程同步技术
         * 他能做什么？
         * 实现线程间的信息共享
         * 他有那些实现方式？
         * 1.阻塞式等待：线程放弃当前剩余的时间片等待另一个线程
         * 2.锁机制：多个线程共享一段数据，但对共享数据的访问通过锁来控制
         * 互斥锁： 只允许同一时间有一个线程访问数据
         * 非排他性锁：运行同一时间有多个锁
         * 3.通知机制：阻塞式等待和锁机制都要求等待的线程轮询被等待数据的状态，轮询会造成cpu 资源的浪费，通知机制允许线程通知其他等待线程改变状态，避免造成cpu 资源的浪费
         * 
         * 
         * 本质上他是什么？
         * 信使，交通灯
         * 
         */

        #region 阻塞式等待
        /*
         * Thread 类中的 Sleep 和join 就是属于这种类型
         * sleep 属于盲目等待一段时间后恢复执行
         * join 等待被调用线程结束后回复执行 和 Task.Wait 差不多 ，join 是一个粗粒度的同步，使用场景较为单一，
         * 对于要将一个大任务分解为多个小任务，让每个线程执行其中的小任务，最后让主线程来汇总时可以用join  当然也可以用Task.Wait()
         * 
         */

        public void 阻塞式等待()
        {
            Thread.Sleep(2000);//阻塞线程 2 秒

            Thread thread = new Thread(() =>
            {
                for (int i = 0; i < 100; i++)
                {
                    Thread.Sleep(100);
                }
            });
            thread.Start();
            thread.Join();

        }


        #endregion

        #region 锁
        /*
         * 在多线程情景下，有时候需要对公共资源进行读写
         * 由于cup 给线程分配的时间片时随机的，所以线程的执行顺序也是随机的，那么他修改或读取的资源时间也是随机的
         * 所以当a 线程 处理某块内存时，很有可能 b 线程也在处理这块内存，当二者同时处理这块内存时，那么这块内存就会变得混乱，从而 a b 线程执行结果也会变得混乱，
         * 我们更希望的时当某个线程执行时他所处理的内存应该就只属于他
         * 为了解决由于线程之间对这块 公共内存 竞争的问题 所以就有了锁的概念
         * 
         * monitor 用户态的锁，只适用于单个进程内的多线程同步访问共享资源的场景
         * monitor.enter() 获取公共资源的访问权并且占用他，如果其他线程正在使用就在当前阻塞（加锁）
         * monitor.exit() 使用完后释放对资源的占用
         * 
         */


        private void 不加锁()
        {
            
            //Task.Factory.StartNew(() =>
            //{
            //    for (int j = 0; j < 100; j++)
            //    {
            //        i++;
            //    }
            //});

            void NoLock()
            {
                int i = 0;
                for (int j = 0; j < 10000; j++)
                {
                    i += i ;
                }
            }

            Thread thread = new Thread(NoLock);
            Thread thread1 = new Thread(NoLock);
            thread.Start();
            thread1.Start();
            thread.Join();
            thread1.Join();

            System.Console.WriteLine($"不加锁的情况下两个线程累加的值{i}");


        }

        
        private static object _lock = new object();
        int i = 0;
        private void 加锁()
        {
           
            void Monitor_Lock()
            {
               
                for (int j = 0; j < 10000; j++)
                {
                    //记录是否获取访问权
                    var lockTaken = false;
                    try
                    {
                        //加锁
                        Monitor.Enter(i, ref lockTaken);
                        i += i;
                    }
                    finally 
                    {
                        //释放锁
                        if (lockTaken==true)
                        {
                            Monitor.Exit(_lock);
                        }
                    }
                   
                }
            }

            void Lock()
            {
                for (int j = 0; j < 10001; j++)
                {
                    lock (_lock)
                    {
                        i++;
                    }
                }
            }

            void Interlock()
            {
                /*
                 * 如果多线程改变的操作对象是一个变量时，那么我们可以把这种操作理解为原子操作
                 * 
                 * 
                 * 
                 * 
                 */

                for (int j = 0; j < 1000; j++)
                {
                    // Interlocked 为多个线程共享的变量提供原子操作。
                    Interlocked.Increment( ref i);
                }
            }
            Thread thread = new Thread(Monitor_Lock);
            Thread thread1 = new Thread(Monitor_Lock);
            thread.Start();
            thread1.Start();
            thread.Join();
            thread1.Join();

            Thread thread2 = new Thread(Lock);
            Thread thread3 = new Thread(Lock);
            thread2.Start();
            thread3.Start();
            thread2.Join();
            thread3.Join();

            //原子操作
            Interlock();
            Thread thread4 = new Thread(Lock);
            Thread thread5 = new Thread(Lock);
            thread4.Start();
            thread5.Start();
            thread4.Join();
            thread5.Join();

        }

        public void 跨进程锁()
        {
            /*
             * mutex 用法和 monitor 类似，不过 mutex 针对的时多进程的线程之间的同步
             */
            var mutex = new Mutex(false,"Glpbal\\");


        }

        #endregion

        #endregion


    }
}
